#include<iostream>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/stat.h>
#include<cassert>
using namespace std;

#define PROCESSA 1 //创建共享内存并且向共享内存写入
#define PROCESSB 0 //从共享内存中读取
#define SIZE 4096
const char* path = ".";
int id = 0x666;

key_t getKey()
{
    return ftok(path, id);
}

int shmGet(key_t key, size_t size, int flag)
{
    umask(0);
    return shmget(key, size, flag);
}

char* attach(int shmid)
{
    char* start = (char*)shmat(shmid, nullptr, 0); //nullptr表示让OS自主选择在存在哪个虚拟地址位置，0表示连接读/写权限
    if(start == (void*)-1)
    {
        perror("attach():");
    }
    return start;
}

class Init
{
public:
    Init(int type)
        :_type(type)
    {
        key_t key = getKey();
        assert(key != -1);
        (void)key;
        //判断是A还是B，是A要创建共享内存，不是A则跳过创建这一步
        if(_type == PROCESSA)
        {
            _shmid = shmGet(key, SIZE, IPC_CREAT | IPC_EXCL | 0666); //IPC_CREAT | IPC_EXCL表示如果不存在该共享内存则创建，如果存在则报错退出，保证该共享内存一定是全新的
            assert(_shmid != -1);
            (void)_shmid;
        }
        else
        {
            //不是A走这一步，不用创建
            _shmid = shmGet(key, SIZE, IPC_CREAT); 
            assert(_shmid != -1);
            (void)_shmid;
        }
        
        //将进程与共享内存之间建立链接
        _start = attach(_shmid);
    }

    ~Init()
    {
        //取消进程与共享内存的链接
        shmdt(_start);
        //因为进程由A创建，所以如果是A要负责释放创建的共享内存，而B只是与共享内存进行链接而已，不需要负责删除，只需要取消与其链接即可
        if(_type == PROCESSA)
        {
            int n = shmctl(_shmid, IPC_RMID, nullptr);
            assert(n != -1);
            (void)n;
        }
    }

public:
    int _type;
    char* _start;
    int _shmid;
};